package com.alibaba.fastjson.parser;

import java.util.Collection;
import java.util.Date;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;

public abstract class AbstractJSONParser implements JSONParser {
	public JSONObject parseObject() {
		JSONObject object = new JSONObject();
		parseObject(object);
		return object;
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public final void parseArray(final Collection array) {
		final JSONLexer lexer = getLexer();
		
		accept(JSONToken.LBRACKET);

		for (;;) {
			if (isEnabled(Feature.AllowArbitraryCommas)) {
				while (lexer.token() == JSONToken.COMMA) {
					lexer.nextToken();
					continue;
				}
			}

			if (lexer.token() == JSONToken.RBRACKET) {
				break;
			}

			Object val = parse();
			array.add(val);

			if (lexer.token() == JSONToken.COMMA) {
				lexer.nextToken();
				continue;
			}
		}

		accept(JSONToken.RBRACKET);
	}
	
	public Object parse() {
		final JSONLexer lexer = getLexer();
		switch (lexer.token()) {
		case LBRACKET:
			JSONArray array = new JSONArray();
			parseArray(array);
			return array;
		case LBRACE:
			JSONObject object = new JSONObject();
			parseObject(object);
			return object;
		case LITERAL_INT:
			Number intValue = lexer.integerValue();
			lexer.nextToken();
			return intValue;
		case LITERAL_FLOAT:

			Object value;
			if (isEnabled(Feature.UseBigDecimal)) {
				value = lexer.decimalValue();
			} else {
				String floatLiteral = lexer.numberString();
				value = Double.parseDouble(floatLiteral);
			}
			lexer.nextToken();
			return value;
		case LITERAL_STRING:
			String stringLiteral = lexer.stringVal();
			lexer.nextToken();

			if (lexer.isEnabled(Feature.AllowISO8601DateFormat)) {
				JSONScanner iso8601Lexer = new JSONScanner(stringLiteral);
				if (iso8601Lexer.scanISO8601DateIfMatch()) {
					return iso8601Lexer.getCalendar().getTime();
				}
			}

			return stringLiteral;
		case NULL:
			lexer.nextToken();
			return null;
		case TRUE:
			lexer.nextToken();
			return Boolean.TRUE;
		case FALSE:
			lexer.nextToken();
			return Boolean.FALSE;
		case NEW:
			lexer.nextToken();
			acceptIdent("Date");
			accept(JSONToken.LPAREN);
			long time = ((Number) lexer.integerValue()).longValue();
			accept(JSONToken.LITERAL_INT);

			accept(JSONToken.RPAREN);

			return new Date(time);
		default:
			throw new JSONException("TODO " + lexer.token().name() + " " + lexer.stringVal());
		}
	}
	
	public void config(Feature feature, boolean state) {
		getLexer().config(feature, state);
	}
	
	public boolean isEnabled(Feature feature) {
		return getLexer().isEnabled(feature);
	}
	
	public abstract JSONScanner getLexer();
	
	public final void accept(final JSONToken token) {
		final JSONLexer lexer = getLexer();
		if (lexer.token() == token) {
			lexer.nextToken();
		} else {
			setErrorEndPos(lexer.pos());
			throw new JSONException("syntax error, expect " + token + ", actual " + lexer.token());
		}
	}

	public final void acceptIdent(String value) {
		final JSONLexer lexer = getLexer();
		if (lexer.token() == JSONToken.IDENTIFIER && value.equals(lexer.stringVal())) {
			lexer.nextToken();
		} else {
			setErrorEndPos(lexer.pos());
			throw new JSONException("syntax error, expect " + value + ", actual " + lexer.stringVal());
		}
	}
	
	private int errorEndPos = -1;

	private void setErrorEndPos(int errPos) {
		if (errPos > errorEndPos)
			errorEndPos = errPos;
	}
	
	public void close() {
		final JSONLexer lexer = getLexer();
		
		if (isEnabled(Feature.AutoCloseSource)) {
			if (!lexer.isEOF()) {
				throw new JSONException("not close json text, token : " + lexer.token());
			}
		}
	}
}
